package com.myk.game.gobangchess;

import com.myk.game.gobangchess.constants.AppConfig;
import com.myk.game.gobangchess.constants.Msger;
import com.myk.game.gobangchess.dates.AccountData;
import com.myk.game.gobangchess.dates.AmountData;
import com.myk.game.gobangchess.dates.NotificationData;
import com.myk.game.gobangchess.dates.PlayingData;
import com.myk.game.gobangchess.signs.CacheSign;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

public class Player implements Runnable {

    private String recentIp;
    private Account account;
    private Socket socket;
    private DataOutputStream dos;
    private DataInputStream dis;
    private PlayerMsgHandler playerMsgHandler;
    public boolean keepFlag;
    private Room enteringRoom; //玩家当前进入的房间
    private final List<CustomBottom> customBottomList; //被动态下发的自定义按钮集合
    private static final Lock lock = new ReentrantLock();

    private Timer autoLeaveTimer; //异常退出超时自动离开任务

    public Player(String ip, Socket socket, DataOutputStream dos, DataInputStream dis, Account account) {
        this.recentIp = ip;
        this.account = account;
        this.socket = socket;
        this.dos = dos;
        this.dis = dis;
        this.playerMsgHandler = new PlayerMsgHandler(this);
        this.customBottomList = new ArrayList<>();
    }

    @Override
    public void run() {
        while (keepFlag) {
            try {
                if (socket.isConnected() && !socket.isClosed()) {
                    handlerMessage(dis.readUTF());
                }
            }
            catch (Exception e) {
                e.printStackTrace();
                System.out.println(getNickNameOrTemp() + "异常断线，30s后自动退出");
                autoLeaveTimer = new Timer();
                autoLeaveTimer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        DataManager.playerLogout(Player.this); //超时了再从列表中移除
                        recycle(); //关闭线程
                    }
                }, Long.parseLong(AppConfig.player_auto_leave_time));
                recycle(); //关闭线程
            }
        }
    }

    //接收到客户端发送的消息后如何处理
    private void handlerMessage(String msg) {
        playerMsgHandler.handlerMessage(msg);
    }

    /**
     * 向客户端发送消息
     */
    public void sendMessage(String type, String value) {
        lock.lock();
        try {
            String msg = Msger.createMsg(type, value);
            try {
                if (socket.isConnected() && !socket.isClosed()) {
                    dos.writeUTF(msg);
                }
            }
            catch (Exception e) {
                e.printStackTrace();
                DataManager.playerLogout(Player.this); //超时了再从列表中移除
                recycle();
            }
        }
        finally {
            lock.unlock();
        }
    }

    //回收该角色的io流与socket，适当延迟10ms防止回收太快产生的异常
    public void recycle() {
        try {
            this.keepFlag = false;
            Thread.sleep(10);
            this.dis.close();
            this.dos.close();
            this.socket.close();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取角色IdCode
     */
    public String getIdCode() {
        return account.getIdCode();
    }

    /**
     * 获取角色显示昵称 （注意！这里获取的昵称只用于显示，因为会被临时昵称所代替。如果需要保存数据，获取账户文件路径等，通过account里的nickName获取）
     */
    public String getNickNameOrTemp() {
        return AccountData.getNickNameOrTemp(account);
    }

    /**
     * 获取角色昵称 （账户实际昵称，不可能是临时昵称）
     */
    public String getNickName() {
        return account.getNickName();
    }

    /**
     * 获取角色账户信息
     */
    public Account getAccount() {
        return account;
    }

    /**
     * 该用户账号是否正常，（非审核中，非被封禁）
     */
    public boolean isAccountNormally() {
        return !AccountData.isAccountUnderReview(account) && !AccountData.isAccountUnderBan(account);
    }

    /**
     * 更新最近登录时间
     */
    public void updateLoginTime() {
        AccountData.updatePlayerLoginTime(account);
    }

    /**
     * 更新个人对弈信息
     */
    public void updatePlayingInfo(Room room, Player opponent, boolean isWin) {
        PlayingData.updatePlayingInfo(account, room, opponent, isWin);
        sendMessage(Msger.refreshPlayerInfo, getPlayerInfo()); //更新角色信息
    }

    /**
     * 获取用户信息
     */
    public String getPlayerInfo() {
        return AccountData.getPlayerInfo(account);
    }

    /**
     * 获取玩家进入的房间
     */
    public Room getEnteringRoom() {
        return enteringRoom;
    }

    /**
     * 返回缓存的ip地址
     */
    public String getRecentIp() {
        return recentIp;
    }

    /**
     * 当角色登录成时执行
     */
    public void onLoginSuccess() {
        this.keepFlag = true;
        new Thread(this).start();
        sendMessage(Msger.loginSuccess, getNickNameOrTemp());
        sendMessage(Msger.refreshPlayerInfo, getPlayerInfo()); //更新角色
        sendMessage(Msger.refreshPlayerListSum, AccountData.getTodayLoginPlayersCountAndCached() + "&" + DataManager.getPlayerListSum() + ""); //更新今日登录玩家数量（除了这里其它地方都从缓存中取） ，更新在线玩家数量
        sendMessage(Msger.refreshRoomList, DataManager.getRoomListInfo()); //更新房间信息
        String notificationIds = NotificationData.getActiveNotificationIds(); //获取所有活跃的公告的id
        if (!notificationIds.equals("-1")) {
            sendMessage(Msger.pushNotificationIds, notificationIds); //推送公告
        }
    }

    /**
     * 设置玩家当前进入的房间
     */
    public void setEnteringRoom(Room room) {
        this.enteringRoom = room;
        //进入房间前清空一下
        clearCustomBottom();
        dispatchCustomBottom();
    }

    /**
     * 玩家离开房间
     */
    public void leaveRoom() {
        if (enteringRoom != null) {
            enteringRoom.playerLeaveRoom(this);
        }
    }

    /**
     * 玩家重连
     */
    public void reConnect(Socket socket, DataOutputStream dos, DataInputStream dis, Account account) {
        if (this.autoLeaveTimer != null) {
            this.autoLeaveTimer.cancel(); //取消自动退出
        }
        this.account = account;
        this.socket = socket;
        this.dos = dos;
        this.dis = dis;
        this.keepFlag = true;
        new Thread(this).start(); //重新开线程

        if (enteringRoom != null) {
            sendMessage(Msger.loginSuccess, getNickNameOrTemp());//通知登录成功客户端开启心跳检测
            enteringRoom.playerReConnectEnter(this);
        }
        else {
            sendMessage(Msger.loginSuccess, getNickNameOrTemp()); //通知登录成功客户端开启心跳检测
            sendMessage(Msger.refreshPlayerInfo, getPlayerInfo()); //更新角色
            sendMessage(Msger.refreshPlayerListSum, AccountData.todayLoginPlayersCountCache + "&" + DataManager.getPlayerListSum()); //更新在线玩家数量
            sendMessage(Msger.refreshRoomList, DataManager.getRoomListInfo()); //更新房间列表信息
        }
    }

    /**
     * 下发自定义按钮
     */
    public void dispatchCustomBottom() {
        if (enteringRoom != null && customBottomList.size() > 0) {
            String titles;
            StringBuilder sb = new StringBuilder();
            for (CustomBottom customBottom : customBottomList) {
                sb.append(customBottom.getTitle()).append(";");
            }
            String s = sb.toString();
            if (s.isEmpty()) {
                titles = "-1";
            }
            else {
                titles = s.substring(0, s.length() - 1);
            }
            sendMessage(Msger.dispatchCustomBottom, titles);
        }
        else {
            sendMessage(Msger.dispatchCustomBottom, "-1");
        }
    }

    /**
     * 注册自定义按钮的监听
     */
    public void registerCustomBottom(CustomBottom... customBottoms) {
        for (CustomBottom customBottom : customBottoms) {
            removeCustomBottom(findCustomBottomByTitle(customBottom.getFrom(), customBottom.getTitle()));//先删除同一房间下旧的同名按钮以免重复触发
            customBottomList.add(customBottom);
        }
        dispatchCustomBottom(); //只要注册了就自动派发
    }

    /**
     * 清除所有自定义按钮
     */
    public void clearCustomBottom() {
        customBottomList.clear();
    }

    /**
     * 移除某个自定义按钮
     */
    public void removeCustomBottom(CustomBottom customBottom) {
        customBottomList.remove(customBottom);
    }

    /**
     * 根据名字获取自定义按钮对象
     */
    public CustomBottom findCustomBottomByTitle(Room room, String findTitle) {
        for (CustomBottom customBottom : customBottomList) {
            if (customBottom.getFrom() == room && findTitle.equals(customBottom.getTitle())) {
                return customBottom;
            }
        }
        return null;
    }

    /**
     * 获取所有自定义按钮（参数只是为了以防万一）
     */
    public List<CustomBottom> getCustomBottomList(Room from) {
        return customBottomList.stream()
                .filter(customBottom -> customBottom.getFrom() == from)
                .collect(Collectors.toList());
    }

    public List<CustomBottom> getCustomBottomList() {
        return customBottomList;
    }

}
